package com.pocket.insight.base.sql.dataset;

import com.pocket.insight.base.mql.Relation;
import com.pocket.insight.base.mql.TypedVal;
import com.pocket.insight.base.mql.relation.StatementRelation;
import com.pocket.insight.base.sql.Vendor;
import com.pocket.insight.controller.dto.DatasetResultDto;
import com.pocket.insight.controller.dto.SqlParam;
import com.pocket.insight.entity.Dataset;
import com.pocket.insight.entity.ME;
import org.apache.commons.collections4.CollectionUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 描述：
 *
 * @author shuize
 * @create 2024/12/4
 */
public class DatasetProxyImpl implements DatasetProxy{

    private DatasetResource datasetResource;

    DatasetProxyImpl(Dataset dataset) {
        this.datasetResource = DatasetResourceFactory.create(dataset);
    }

    Dataset getDataset() {
        return this.datasetResource.getDataset();
    }

    @Override
    public DatasetResultDto fetchFieldsData(List<Dataset.Field> fields, SqlParam sqlParam) {
        return fetchDatasetQueryData(fieldsToQuery(fields, sqlParam, false));
    }

    private DatasetResultDto fetchFieldsData(List<Dataset.Field> fields, SqlParam sqlParam, boolean ignoreCastError) {
        return fetchDatasetQueryData(fieldsToQuery(fields, sqlParam, false, false, ignoreCastError));
    }

    private DatasetResultDto fetchDatasetQueryData(TypedVal query) {
        return this.datasetResource.getVendor().fetchQueryData(query);
    }

    @Override
    public DatasetResource getDatasetResource() {
        return this.datasetResource;
    }

    @Override
    public DatasetResultDto fetchDatasetData(SqlParam sqlParam) {
        List<Dataset.Field> schema = this.datasetResource.getDatasetRelation().getSchemaProvider().getFields();
        Vendor vendor = this.datasetResource.getVendor();
        if (schema == null) {
            schema = this.datasetResource.getVendor().getTableSchema(getDataset().getOptions().getTable());
            this.datasetResource.setSchema(schema);
        }

        DatasetResultDto datasetResultDto;
        if (!vendor.isSqlAvailable()) {
            datasetResultDto = vendor.fetchTableData(this.datasetResource.getDataset().getOptions().fetchTableFullPath());
        } else {
            datasetResultDto = fetchFieldsData(Dataset.fetchVisibleFields(schema), sqlParam);
        }

        datasetResultDto.setSchema(DatasetUtil.mergeSchemaKeepFieldsOrder(getSchemaMap(), datasetResultDto.getSchema(), vendor.isFieldCaseSensitive()));
        return datasetResultDto;
    }

    @Override
    public TypedVal fieldsToQuery(List<Dataset.Field> fields, SqlParam sqlParam, Boolean forCastType) {
        return fieldsToQuery(fields, sqlParam, forCastType, false, true);
    }

    private TypedVal fieldsToQuery(List<Dataset.Field> fields, SqlParam sqlParam, Boolean forCastType, boolean labelAsAlias, boolean ignoreCastError) {
        Relation datasetRelation = forCastType.booleanValue() ? this.datasetResource.getRelation() : this.datasetResource.getRelation(fields);

        StatementRelation relation = (datasetRelation instanceof StatementRelation) ? ((StatementRelation)datasetRelation).copyRelation().alias(null) : Relation.statement(datasetRelation.getDialect()).scan(datasetRelation);

        CollectionUtils.emptyIfNull(fields).forEach(field -> relation.project(ME.create(ME.Kind.field, field.getFieldName(), null, labelAsAlias ? field.fetchLabelOrFieldName() : field.getFieldName()).setType(field.fetchType())));

        relation.sqlParam(sqlParam);

        return Relation.evalWithRewrite(relation, this.datasetResource.getVendor().getDialect());
    }

    public Map<String, Dataset.Field> getSchemaMap() {
        return getDataset().fetchSchemaMap();
    }


}
